package com.puzzlebazar.client.squaregrid.control;

import com.google.gwt.event.dom.client.MouseEvent;
import com.google.gwt.event.shared.EventHandler;
import com.puzzlebazar.client.squaregrid.view.ComponentHighlighter;
import com.puzzlebazar.client.squaregrid.view.HighlightConstraints;
import com.puzzlebazar.client.util.Observable;
import com.puzzlebazar.client.util.Observer;
import com.puzzlebazar.client.util.Recti;
import com.puzzlebazar.client.util.Vec2i;

public class HighlightControl implements Observer {

	private final HighlightConstraints model;
	private final ComponentHighlighter highlighter;
	private final ControlView controller;
	
	private final CellCallback cellCallback = new CellCallback();
	private final VerticalEdgeCallback verticalEdgeCallback = new VerticalEdgeCallback();
	private final HorizontalEdgeCallback horizontalEdgeCallback = new HorizontalEdgeCallback();
	private final VertexCallback vertexCallback = new VertexCallback();
	
	private boolean enabled = true;
	
	/**
	 * Creates a controller that can automatically highlight cells, edges and vertices
	 * as the user moves his mouse around
	 * 
	 * @param model The model to use to identify highlighting constraints
	 * @param highlighter The object responsible of highlighting the various components
	 * @param controller The controller decorator to use
	 */
	public HighlightControl(
			HighlightConstraints model,
			ComponentHighlighter highlighter,
			ControlView controller) {
		
		this.model = model;
		this.highlighter = highlighter;
		this.controller = controller;
		
		controller.addCellCallback( cellCallback );
		controller.addVerticalEdgeCallback( verticalEdgeCallback );
		controller.addHorizontalEdgeCallback( horizontalEdgeCallback );
		controller.addVertexCallback( vertexCallback );
	
		model.addObserver( this );	
	}


	private class CellCallback implements ComponentCallback {
	
		@Override
		public boolean mouseDown(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean mouseUp(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean canHandleMouseEnter(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			return enabled && model.canHandleCell(cellLoc);
		}

		@Override
		public boolean mouseEnter(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			if( !enabled || !model.canHandleCell(cellLoc) ) return false;
			try {
				Recti cellGroup = model.cellGroup(cellLoc);
				if( cellGroup == null )
					highlighter.highlightCell( cellLoc );
				else
					highlighter.highlightCells(
							cellGroup.getCell00(), 
							cellGroup.getCell11());
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return true;
		}

		@Override
		public boolean mouseLeave(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			highlighter.unhilightAll();
			return true;
		}

	}
	
	private class VertexCallback implements ComponentCallback {

		@Override
		public boolean mouseDown(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean mouseUp(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}


		@Override
		public boolean canHandleMouseEnter(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			return enabled && model.canHandleVertex(vertexLoc);
		}

		@Override
		public boolean mouseEnter(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			if( !enabled || !model.canHandleVertex(vertexLoc) ) return false;
			try {
				highlighter.highlightVertex( vertexLoc );
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return true;
		}

		@Override
		public boolean mouseLeave(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			highlighter.unhilightAll();
			return true;
		}
	
	}

	private class HorizontalEdgeCallback implements ComponentCallback {

		@Override
		public boolean mouseDown(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean mouseUp(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean canHandleMouseEnter(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			return enabled && model.canHandleHorizontalEdge(edgeLoc);
		}

		@Override
		public boolean mouseEnter(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			if( !enabled || !model.canHandleHorizontalEdge(edgeLoc) ) return false;
			try {
				highlighter.highlightHorizontalEdge( edgeLoc );
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return true;
		}

		@Override
		public boolean mouseLeave(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			highlighter.unhilightAll();
			return true;
		}

	}
	
	private class VerticalEdgeCallback implements ComponentCallback {

		@Override
		public boolean mouseDown(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean mouseUp(Vec2i vertexLoc, MouseEvent<? extends EventHandler> event ) {
			return false;
		}

		@Override
		public boolean canHandleMouseEnter(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			return enabled && model.canHandleVerticalEdge(edgeLoc);
		}

		@Override
		public boolean mouseEnter(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			if( !enabled || !model.canHandleVerticalEdge(edgeLoc) ) return false;
			try {
				highlighter.highlightVerticalEdge( edgeLoc );
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return true;
		}

		@Override
		public boolean mouseLeave(Vec2i edgeLoc, MouseEvent<? extends EventHandler> event ) {
			highlighter.unhilightAll();
			return true;
		}

	}

	@Override
	public void update(Observable observable, Object arg) {
		if( controller != null) {
			highlighter.unhilightAll();
// TODO Possibily rehighlight everything
//			controller.regenerateEnterEvents();
		}
	}

	/**
	 * Turn highlighting on and off
	 * 
	 * @param enabled True to turn on, false otherwise
	 */
	public void enable(boolean enabled) {
		this.enabled = enabled;
		if( !enabled )
			highlighter.unhilightAll();
// TODO Possibily rehighlight everything
//		else 
//			controller.regenerateEnterEvents();
	}


	@Override
	public void release() {
		model.deleteObserver( this );
		
		controller.removeCellCallback( cellCallback );
		controller.removeVerticalEdgeCallback( verticalEdgeCallback );
		controller.removeHorizontalEdgeCallback( horizontalEdgeCallback );
		controller.removeVertexCallback( vertexCallback );
	}

	
}